DB_SCAN_RANGE = 360        #Period of time in which the apps should be added to the hash
DB_SCAN_RANGE_ENSURE = 60
DB_SCAN_INTERVAL = 300     #Time interval between every database scan
HASH_SCAN_INTERVAL = 30    #Time interval between every hash scan


module CloudController
  class << self

    def setup_updates
      @timestamp = Time.now
      @current_num_requests = 0
      EM.add_periodic_timer(1) { CloudController.update_requests_per_sec }
    end

    def update_requests_per_sec
      # Update our timestamp and calculate delta for reqs/sec

      now = Time.now
      delta = now - @timestamp
      @timestamp = now
      # Now calculate Requests/sec
      new_num_requests = VCAP::Component.varz[:requests]
      VCAP::Component.varz[:requests_per_sec] = ((new_num_requests - @current_num_requests)/delta.to_f).to_i
      @current_num_requests = new_num_requests
      #CloudController.logger.info("***********************#{Time.now}**************************")

    end
  end
end

###############start or stop apps in a particular time in the future#############
###############                    WuTong @ 2013.10                 #############
module MissionPlan
  class << self

    def mission_app_find(name)                                      #find complete information of an app by its name
      mission_user_list = ::User.all
      mission_user_list.each{|user|
        if  user.apps_owned.find_by_name(name)
            mission_app_selected = user.apps_owned.find_by_name(name)
            #CloudController.logger.info("%%%%%%%%%%%%%%%%%%%%%%#{mission_app_selected.inspect}")
            return mission_app_selected
        end
      }
    end


    def app_catch(app_time, present_time)                          #if the start/stop time of an app match our demands, catch it!
      if app_time != nil
        return true if Integer(app_time) >= (present_time - DB_SCAN_RANGE_ENSURE) && Integer(app_time) <= (present_time + DB_SCAN_RANGE)     #Settle the period of time
      end
    end

    #Scan the database, fetch apps whose start_time is in the settled period of time and store them in the hash

    def db_scan(hash_start, hash_stop)                             #scan the database, put target apps into the hash
      hash_start.clear
      hash_stop.clear
      present_time = Time.now.to_i
      app_all = ::App.all
      app_all.each do |ins|
        if app_catch(ins.app_starttime, present_time)     #Settle the period of time
          hash_start[ins.name] = Integer(ins.app_starttime)

          #CloudController.logger.info("***************#{hash_start}*******************")
        end


        if app_catch(ins.app_stoptime, present_time)     #Settle the period of time
          hash_stop[ins.name] = Integer(ins.app_stoptime)
        end
      end
    end

   #Scan the hash, fetch the apps whose start_time matches present time, start them!
    def hash_scan(hash, option)                                      #scan the hash, start/stop the apps on the set time
      hash.each do |k, v|
        present_time = Time.now.to_i
        if v >= (present_time - present_time % 60) && v <= (present_time + 60)
          app_selected = mission_app_find(k)
          #CloudController.logger.info("******app_selected**********#{app_selected.name}************")
          mission_update(app_selected, option)
        end
      end
    end

    def check_has_capacity_for?(app, previous_state)                 #check whether there is enough capacity for the app to start

      owner = app.owner
      ## If we are creating this app, check to make sure the user
      ## has not allocated too many apps, regardless of state.
      #if app.new_record?
      #  if current_apps = owner.no_more_apps?
      #    app_quota = owner.account_capacity[:apps]
      #    raise CloudError.new(CloudError::ACCOUNT_APPS_TOO_MANY, current_apps, app_quota)
      #  end
      #end

      # Only worry about apps that are running or they want to run.
      return true unless app.state =~ /STARTED/i

      # Whether it is a creation or an update, check memory capacity.
      #memory    = app.memory
      instances = app.instances
      per_instance = app.memory
      existing = (previous_state =~ /STARTED/i) ? app.total_memory : 0

      unless owner.has_memory_for?(instances, per_instance, existing, previous_state)
        mem_quota = owner.account_capacity[:memory]
        raise CloudError.new(CloudError::ACCOUNT_NOT_ENOUGH_MEMORY, "#{mem_quota}M")
      end
    end

    def mission_update(app, option)                                  #start/stop the apps by change the state value

      previous_state = app.state
      case option
        when /TO_START/i
          if previous_state == 'STOPPED'
            app.state = 'STARTED'
          end
        when /TO_STOP/i
          if previous_state == 'STARTED'
            app.state = 'STOPPED'
          end
      end
      check_has_capacity_for?(app, previous_state)
      changed = app.changed

      begin
        app.save!
      rescue Exception => e
        CloudController.logger.error "app: #{app.id} Failed to save new app errors: #{app.errors}.  Exception: #{e}"
        raise CloudError.new(CloudError::APP_INVALID)
      end

      # This needs to be called after the app is saved, but before staging.
      app.save if app.changed?

      # Process any changes that require action on out part here.
      manager = AppManager.new(app)
      #stage_app(app) if app.needs_staging?

      if changed.include?('state')
        if app.stopped?
          manager.stopped
        elsif app.started?
          manager.started
        end
        manager.updated
      end
      #CloudController.logger.info("**********mission_update****#{app.state}*********")
      yield(app) if block_given?
    end

    def mission
      hash_start = Hash.new
      hash_stop = Hash.new

      db_scan(hash_start, hash_stop)     #start db_scan immediately when CF started
      EM.add_periodic_timer(DB_SCAN_INTERVAL){db_scan(hash_start, hash_stop)}
      CloudController.logger.info("%%%%%dbscan%%%%%%#{hash_start}%%%#{hash_stop}%%%%%%%%%%")
      EM.add_periodic_timer(HASH_SCAN_INTERVAL) {
        hash_scan(hash_start, "TO_START")
        hash_scan(hash_stop, "TO_STOP")}

    end
  end
end

# Initialize varzs

EM.next_tick do
  VCAP::Component.varz[:requests] = 0
  VCAP::Component.varz[:pending_requests] = 0
  VCAP::Component.varz[:requests_per_sec] = 0
  VCAP::Component.varz[:running_stage_cmds] = 0
  VCAP::Component.varz[:pending_stage_cmds] = 0
  CloudController.setup_updates
  MissionPlan.mission

end
